package io.lvdaxian.mybatis18.executor;

import io.lvdaxian.mybatis18.cache.CacheKey;
import io.lvdaxian.mybatis18.cache.impl.PerpetualCache;
import io.lvdaxian.mybatis18.mapping.BoundSql;
import io.lvdaxian.mybatis18.mapping.MappedStatement;
import io.lvdaxian.mybatis18.mapping.ParameterMapping;
import io.lvdaxian.mybatis18.reflection.MetaObject;
import io.lvdaxian.mybatis18.session.Configuration;
import io.lvdaxian.mybatis18.session.LocalCacheScope;
import io.lvdaxian.mybatis18.session.ResultHandler;
import io.lvdaxian.mybatis18.session.RowBounds;
import io.lvdaxian.mybatis18.transaction.Transaction;
import io.lvdaxian.mybatis18.type.TypeHandlerRegistry;
import org.slf4j.LoggerFactory;

import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

/**
 * 执行器 公共的方法都是这里维护
 *
 * @author lihh
 */
public abstract class BaseExecutor implements Executor {
  private final org.slf4j.Logger logger = LoggerFactory.getLogger(BaseExecutor.class);
  
  // 配置类 其实会发现配置类 贯穿整个上下文
  protected Configuration configuration;
  // 事务
  protected Transaction transaction;
  // 执行器
  protected Executor wrapper;
  // 本地缓存
  protected PerpetualCache localCache;
  private boolean closed;
  // 查询堆栈
  protected int queryStack = 0;
  
  public BaseExecutor(Configuration configuration, Transaction transaction) {
    this.configuration = configuration;
    this.transaction = transaction;
    this.wrapper = this;
    this.localCache = new PerpetualCache("LocalCache");
  }
  
  /**
   * 这里 创建缓存 key
   *
   * @param ms              mappedStatement
   * @param parameterObject 参数对象
   * @param rowBounds       分页相关信息
   * @param boundSql        sql
   * @return 缓存 key
   * @author lihh
   */
  @Override
  public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    // 判断是否关闭
    if (closed) {
      throw new RuntimeException("Executor was closed.");
    }
    
    // 构建 缓存key
    CacheKey cacheKey = new CacheKey();
    // mappedStatement Id
    cacheKey.update(ms.getId());
    // 分页信息
    cacheKey.update(rowBounds.getOffset());
    cacheKey.update(rowBounds.getLimit());
    // sql 信息
    cacheKey.update(boundSql.getSql());
    // 这是 参数映射
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    // 类型事件注册器
    TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
    
    // 遍历 参数映射器
    for (ParameterMapping parameterMapping : parameterMappings) {
      Object value;
      // 属性名称
      String propertyName = parameterMapping.getProperty();
      if (boundSql.hasAdditionalParameter(propertyName)) {
        value = boundSql.getAdditionalParameter(propertyName);
      } else if (parameterObject == null) {
        value = null;
      } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
        value = parameterObject;
      } else {
        MetaObject metaObject = configuration.newMetaObject(parameterObject);
        value = metaObject.getValue(propertyName);
      }
      // 更新的 sql where 参数
      cacheKey.update(value);
    }
    if (configuration.getEnvironment() != null) {
      // 这里更新环境信息
      cacheKey.update(configuration.getEnvironment().getId());
    }
    return cacheKey;
  }
  
  @Override
  public void setExecutorWrapper(Executor executor) {
    this.wrapper = wrapper;
  }
  
  @Override
  public Transaction getTransaction() {
    if (closed) throw new RuntimeException("Executor was closed.");
    return transaction;
  }
  
  @Override
  public void rollback(boolean required) throws SQLException {
    if (!closed) {
      try {
        // 如果是rollback 的话，也是需要清空缓存的
        clearLocalCache();
      } finally {
        if (required) {
          transaction.rollback();
        }
      }
    }
  }
  
  @Override
  public void clearLocalCache() {
    if (!closed) {
      localCache.clear();
    }
  }
  
  protected void closeStatement(Statement statement) {
    if (statement != null) {
      try {
        statement.close();
      } catch (SQLException ignore) {
      }
    }
  }
  
  
  @Override
  public void close(boolean forceRollback) {
    try {
      try {
        rollback(forceRollback);
      } finally {
        transaction.close();
      }
    } catch (SQLException e) {
      logger.warn("Unexpected exception on closing transaction.  Cause: " + e);
    } finally {
      transaction = null;
      closed = true;
    }
  }
  
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    // 通过 param 拿到 sql
    BoundSql boundSql = ms.getBoundSql(parameter);
    // 2. 创建缓存Key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
  }
  
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    if (closed) {
      throw new RuntimeException("Executor was closed.");
    }
    // 清理局部缓存，查询堆栈为0则清理。queryStack 避免递归调用清理
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    
    List<E> list;
    try {
      queryStack++;
      // 根据cacheKey从localCache中查询数据
      // 这里的key 是 缓存的key
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      // 如果 list == null 为null的话，直接通过 doQuery 进行查询
      if (null == list) {
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      // 判断 localCacheScope 的状态为 STATEMENT 的话，清空 local cache
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        clearLocalCache();
      }
    }
    return list;
  }
  
  private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    // 这是一个占位符 设置
    localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);
    try {
      // 这是 查询数据的操作
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    
    // 存入缓存
    localCache.putObject(key, list);
    return list;
  }
  
  /**
   * 该方法由 各自的继承者来实现
   *
   * @param ms            statement 语句
   * @param parameter     参数
   * @param resultHandler 结果handler
   * @param boundSql      待执行sql
   * @param <E>           动态参数
   * @return 返回集合
   * @author lihh
   */
  protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql);
  
  @Override
  public int update(MappedStatement ms, Object parameter) throws SQLException {
    if (closed) {
      throw new RuntimeException("Executor was closed.");
    }
    
    // 更新的时候 清空缓存
    clearLocalCache();
    // 直接 更新处理
    return doUpdate(ms, parameter);
  }
  
  // 这里是 实际的更新处理
  protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;
  
  @Override
  public void commit(boolean required) throws SQLException {
    if (closed)
      throw new RuntimeException("Cannot commit, transaction is already closed");
    
    // 执行 commit 处理的时候，直接清空缓存状态
    clearLocalCache();
    if (required) transaction.commit();
  }
}
